﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace FizzDevelop
{
	public enum Language
	{
		Plaintext, Assembly, CSharp, HTML, JavaScript, Java,
		MSSQL, PostgreSQL, Python, VBScript, XML
	}

	public enum PlatformType { Compiled, Managed, Interpreted }

	public enum LanguageType { Markup, Programming, HostedScripting, Query, None }

	public enum Success { Successful, Failed, Aborted }

	public class BuildStatus
	{
		public Success Success;
		public string Error;
		public string Output;
		public List<string> PreparedFiles;
	}

	public delegate BuildStatus Build(List<string> files, string args);
	public delegate Success Run(List<string> files, string args);

	public class Platform
	{
		public PlatformType Type;
		public string DisplayName;
		public Build Build;
		public Run Run;
	}

	class LanguageInfo
	{
		public string ScintillaKey;
		public string DisplayName;
		public LanguageType Type;
		public List<Platform> Platforms;
	}

	static class Languages
	{
		public static Dictionary<Language, LanguageInfo> LanguageList = new Dictionary<Language, LanguageInfo>();
		public static string JavaPath = @"C:\Program Files\Java\jdk1.7.0_07\bin";
		public static string BuildError;
		public static string BuildOutput;

		static Languages()
		{
			LanguageList.Add(Language.Java, new LanguageInfo
			{
				ScintillaKey = "js",
				DisplayName = "Java",
				Type = LanguageType.Programming,
				Platforms = new List<Platform>
				{
					new Platform
					{
						Type = PlatformType.Managed,
						DisplayName = "Java VM",
						Build = delegate(List<string> files, string args)
						{ 
							BuildError = "";
							BuildOutput = "";
							ProcessStartInfo buildInfo = new ProcessStartInfo();
							buildInfo.WorkingDirectory = Path.GetDirectoryName(files[0]);
							buildInfo.CreateNoWindow = true;
							buildInfo.UseShellExecute = false;
							buildInfo.RedirectStandardOutput = true;
							buildInfo.RedirectStandardError = true;
							buildInfo.WindowStyle = ProcessWindowStyle.Hidden;
							if (String.IsNullOrEmpty(JavaPath))
							{
								string path = Util.GetFolder("Please select the folder where javac.exe and java.exe are located:");
								if (!String.IsNullOrEmpty(path)) JavaPath = path; else return new BuildStatus {Success = Success.Aborted};
							}
							buildInfo.FileName = JavaPath + "\\javac.exe";
							buildInfo.Arguments = 
								"-d " + Path.GetDirectoryName(files[0]) + "\\bin\\ "
								+ files[0];
							Process buildProcess = new Process();
							buildProcess.StartInfo = buildInfo;
							buildProcess.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e) {BuildError += e.Data;};
							buildProcess.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e) {BuildOutput += e.Data;};
							bool started = buildProcess.Start();
							if (!started) return new BuildStatus {Success = Success.Failed, Error = "Failed to launch Java compiler (javac.exe)"};
							buildProcess.BeginErrorReadLine();
							buildProcess.BeginOutputReadLine();
							buildProcess.WaitForExit();
							int exitCode = buildProcess.ExitCode;
							buildProcess.Close();
							if (String.IsNullOrWhiteSpace(BuildOutput)) BuildOutput = "    *Empty*    ";
							if (String.IsNullOrWhiteSpace(BuildError)) BuildError = "    *Empty*    ";
							if (exitCode == 0)
								return new BuildStatus { Success = Success.Successful, PreparedFiles = new List<string> { Path.GetDirectoryName(files[0]) + "\\bin\\" + Path.GetFileNameWithoutExtension(files[0]) + ".class" } };
							else
								return new BuildStatus { Success = Success.Failed, Error = BuildError, Output = BuildOutput };
						},
						Run = delegate(List<string> files, string args)
						{
							ProcessStartInfo runInfo = new ProcessStartInfo();
							runInfo.WorkingDirectory = Path.GetDirectoryName(files[0]) + "\\bin";
							runInfo.CreateNoWindow = true;
							runInfo.UseShellExecute = false;
							runInfo.RedirectStandardOutput = true;
							runInfo.RedirectStandardError = true;
							runInfo.WindowStyle = ProcessWindowStyle.Hidden;
							if (String.IsNullOrEmpty(JavaPath))
							{
								string path = Util.GetFolder("Please select the folder where javac.exe and java.exe are located:");
								if (!String.IsNullOrEmpty(path)) JavaPath = path; else return Success.Failed;
							}
							runInfo.FileName = JavaPath + "\\java.exe";
							runInfo.Arguments = Path.GetFileNameWithoutExtension(files[0]);
							Process runProcess = new Process();
							runProcess.StartInfo = runInfo;
							bool started = runProcess.Start();
							if (!started) return Success.Failed; else return Success.Successful;
						}
					}
				}
			});
			LanguageList.Add(Language.CSharp, new LanguageInfo
			{
				ScintillaKey = "cs",
				DisplayName = "C#",
				Type = LanguageType.Programming,
				Platforms = new List<Platform>
				{
					new Platform
					{
						Type = PlatformType.Compiled,
						DisplayName = ".NET Framework",
						Build = delegate(List<string> files, string args)
						{
							// Note: This is quick and dirty, definitely not production ready.
							CSharpCodeProvider compiler = new CSharpCodeProvider();
							CompilerParameters options = new CompilerParameters(new String[] {"System.dll", "System.Windows.Forms.dll"});
							options.GenerateExecutable = true;
							options.OutputAssembly = Path.GetDirectoryName(files[0]) + "\\bin\\" + Path.GetFileNameWithoutExtension(files[0]) + ".exe";
							CompilerResults results = compiler.CompileAssemblyFromFile(options, files.ToArray());
							if (results.NativeCompilerReturnValue == 0)
							{
								return new BuildStatus { Success = Success.Successful, PreparedFiles = new List<string> {results.PathToAssembly}};
							}
							else
								return new BuildStatus { Success = Success.Failed, Error = results.Errors[0].ErrorText, Output = results.Output[0] };
						}
					}
				}
			});
			LanguageList.Add(Language.Plaintext, new LanguageInfo
			{
				ScintillaKey = "default",
				DisplayName = "Plaintext",
				Type = LanguageType.None
			});
			LanguageList.Add(Language.Assembly, new LanguageInfo
			{
				ScintillaKey = "asm",
				DisplayName = "Assembly",
				Type = LanguageType.Programming
			});
			LanguageList.Add(Language.HTML, new LanguageInfo
			{
				ScintillaKey = "html",
				DisplayName = "HTML",
				Type = LanguageType.Markup
			});
			LanguageList.Add(Language.JavaScript, new LanguageInfo
			{
				ScintillaKey = "js",
				DisplayName = "JavaScript",
				Type = LanguageType.HostedScripting
			});
			LanguageList.Add(Language.MSSQL, new LanguageInfo
			{
				ScintillaKey = "mssql",
				DisplayName = "MS-SQL",
				Type = LanguageType.Query
			});
			LanguageList.Add(Language.PostgreSQL, new LanguageInfo
			{
				ScintillaKey = "psql",
				DisplayName = "Postgre SQL",
				Type = LanguageType.Query
			});
			LanguageList.Add(Language.Python, new LanguageInfo
			{
				ScintillaKey = "python",
				DisplayName = "Python",
				Type = LanguageType.Programming
			});
			LanguageList.Add(Language.VBScript, new LanguageInfo
			{
				ScintillaKey = "vbscript",
				DisplayName = "VBScript",
				Type = LanguageType.Programming
			});
			LanguageList.Add(Language.XML, new LanguageInfo
			{
				ScintillaKey = "xml",
				DisplayName = "XML",
				Type = LanguageType.Markup
			});
		}

		public static int CompareLanguagesByTitle(ToolStripItem x, ToolStripItem y)
		{
			return String.Compare(x.Text, y.Text);
		}
	}
}
